<?php
include 'util/spyc.php';
include 'secure/jwt.php';
include 'database/db.php';

/**
 * 单点登录功能类,有以下功能
 * 1. 签发Token
 * 2. 验证Token
 * 3. 收回Token
 */
class SSO
{
    public $DB;
    public $ENV;
    public function __construct()
    {
        $this->DB = new DB();
        $this->ENV = spyc_load_file($_SERVER['DOCUMENT_ROOT'].'/server/properties/env.yml');
    }

    /**
     * 验证Token
     * @param  [Token] $token [需要验证的Token]
     * @return [array]        [code=>0/1 Token无效/有效]
     */
    public function verify($token)
    {
        $code = 0;
        $items = explode('.', $token);
        $key    = md5(($this->ENV['secret']['signKey'])['0']);

        if (count($items) != 3) {
            goto end;
        }

        list($header, $payload, $sign) = $items;

        $headerDecode = json_decode(base64_decode($header), true);
        if (empty($headerDecode['alg'])) {
            goto end;
        }

        if (JWT::signature($header . '.' . $payload, $key, $headerDecode['alg']) !== $sign) {
            goto end;
        }

        $payload = json_decode(base64_decode($payload), true);

        $time = time();
        if (isset($payload['timestamp']) && $payload['timestamp'] > $time) {
            goto end;
        }

        if (isset($payload['expire']) && $payload['expire'] < $time) {
            $this->destory($token);
            goto end;
        }

        $code = 1;

        end:
        return array('code' => $code);
        ;
    }

    /**
     * 生成Token
     * @param  [string] $username [用户名]
     * @param  [string] $password [密码]
     * @return [array]           [array('code'=>0/1/-1 密码错误/成功/用户不存在,'token'=>Token)]
     */
    public function token($username, $password, $verifiCode)
    {
        $querySql = "select id, password from user where name=?";
        $paramTypeStr = 's';
        $params = array(&$username);
        $resultSet = $this->DB->select($querySql, $paramTypeStr, $params);
        if ($resultSet) {
            // 用户存在，则判断密码是否正确
            $userId = $resultSet[0]['id'];
            $dbPassword = $resultSet[0]['password'];
            if ($password == sha1($dbPassword.$verifiCode)) {
                $tokenExist = $this->search($userId);
                if ($tokenExist) {
                    // 每次都重新生成Token
                    $this->destory($tokenExist);
                }
                // 生成JWT(Json Web Token)给客户端
                $jwt = new JWT($userId, time()+60, ($this->ENV['secret']['signKey'])['0']);
                $token = $jwt->token();
            } else {
                // 密码错误，code->0
                return array('code' => 0, 'token'=> null );
            }
        } else {
            // 用户不存在 code->-1
            return array('code' => -1, 'token'=> null );
        }
        $this->save($userId, $token);  // 保存Token到数据库
        return array('code' => 1, 'token'=> $token );
    }

    /**
     * 销毁Token
     * @param  [int] $token [Token ID]
     * @return [array]        [code=>0/1 销毁失败/成功]
     */
    public function destory($token)
    {
        $delteSql = "DELETE FROM token WHERE id=?";
        $paramTypeStr = 'i';
        $params = array(&$token);
        $result = $this->DB->update($delteSql, $paramTypeStr, $params);
        if ($result) {
            return array('code' => 1, );
        } else {
            return array('code' => 0, 'msg'=> 'token销毁失败' );
        }
    }

    /**
     * 保存Token到数据库
     * @param  [string] $token [Token]
     * @return [array]        [code=>0/1，保存失败/成功,msg=>失败信息]
     */
    public function save($userId, $token)
    {
        $addSql = "INSERT INTO token(userId,token) VALUES (?,?)";
        $paramTypeStr = 'is';
        $params = array(&$userId,&$token);
        $result = $this->DB->update($addSql, $paramTypeStr, $params);
        if ($result) {
            return array('code' => 1, );
        } else {
            return array('code' => 0, 'msg'=> 'token保存失败' );
        }
    }

    /**
     * 查找用户Token是否已经存在
     * @param  [type] $token [Token]
     * @return [type]        [tokenID/null]
     */
    public function search($userId)
    {
        $searchSql = "SELECT id FROM token WHERE userId=?";
        $paramTypeStr = 's';
        $params = array(&$userId);
        $resultSet = $this->DB->select($searchSql, $paramTypeStr, $params);
        if ($resultSet) {
            if (count($resultSet)>1) {
                // 一个用户对应的token大于一个
            } else {
                return $resultSet[0]['id'];
            }
        } else {
            return null;
        }
    }

    public function __destruct()
    {
        if ($this->DB) {
            $this->DB->close();
        }
    }
}
